package com.shuimin.as.util;

import pond.common.PATH;
import pond.common.S;
import pond.common.STREAM;
import pond.common.STRING;
import pond.db.Prototype;
import pond.db.Record;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by ed on 6/24/16.
 */
public class FrontScaffoldBuilder {

  static final File scaffold_root = new File(PATH.classpathRoot(), "scaffold");
  final String type;
  final String tpl_html;
  final String tpl_aggrid;
  final String tpl_controller;
  final Record model;

  final static String model_prefix = "com.shuimin.as.model.";

  final String class_name;
  final String[] class_paths;

  final File browserify_location = new File(PATH.detectWebRootPath()
                                                + File.separator + "browserify.sh");

  final File angular_main_location = new File(PATH.detectWebRootPath()
                                                  + File.separator + "www" + File.separator + "main.js");
  final String underscore_name;


  public FrontScaffoldBuilder(String type, String cls_name, String alias) throws ClassNotFoundException {
    this.class_name = cls_name;
    class_paths = cls_name.split("\\.");

    if(alias !=null && class_paths.length > 0)class_paths[class_paths.length - 1] = alias;

    this.type = type;
    Class<Record> proto_class = (Class<Record>) Class.forName(model_prefix + cls_name);
    this.model = Prototype.proto(proto_class);
    this.underscore_name = S.avoidNull(alias, STRING.underscore(cls_name));

    File _dir = new File(scaffold_root, type);
    try (
        FileInputStream _html = new FileInputStream(new File(_dir, "model.html"));
        FileInputStream _aggrid =
            new FileInputStream(new File(_dir, "model.aggrid.js.tpl"));
        FileInputStream _controller =
            new FileInputStream(new File(_dir, "model.controller.js.tpl"))
    ) {

      this.tpl_html = STREAM.readFully(_html, Charset.defaultCharset());
      this.tpl_aggrid = STREAM.readFully(_aggrid, Charset.defaultCharset());
      this.tpl_controller = STREAM.readFully(_controller, Charset.defaultCharset());

    } catch (IOException e) {
      e.printStackTrace();
      throw new RuntimeException("fatal");
    }
  }

  File get_or_write_model_dir_in_www() throws IOException {
    File module_dir = new File(
        PATH.detectWebRootPath()
            + File.separator + "www"
            + File.separator + "modules"
            + String.join("", S._for(class_paths).map(path -> File.separator + path).join())
    );

    if (!module_dir.exists()) {
      if (!module_dir.mkdirs()) throw new IOException("mkdir failed");
    }

    return module_dir;
  }

  File get_or_write_model_dir_in_js_app() throws IOException {
    File module_dir = new File(
        PATH.detectWebRootPath()
            + File.separator + "js-app"
            + String.join("", S._for(class_paths).map(path -> File.separator + path).join())
    );

    if (!module_dir.exists()) {
      if (!module_dir.mkdirs()) throw new IOException("mkdir failed");
    }

    return module_dir;
  }

  String build_content_add() {
    StringBuilder html = new StringBuilder("<div class=\"new-block\" >\n");
    S._for(model.declaredFieldNames()).each(fname -> {
      html.append("\t<div class=\"new-item\">\n")
          //TOOD i18n
          .append("\t\t<span>" + fname + "</span>\n")
          .append("\t\t<input ng-model=\"record_add." + fname + "\" />\n")
          .append("\t</div>\n");
    });
    html.append("</div>\n");
    return html.toString();
  }

  String build_content_edit() {
    StringBuilder html = new StringBuilder("<div class=\"new-block\" >\n");
    S._for(model.declaredFieldNames()).each(fname -> {
      html.append("\t<div class=\"new-item\">\n")
          //TOOD i18n
          .append("\t\t<span>" + fname + "</span>\n")
          .append("\t\t<input ng-model=\"record_edit." + fname + "\" />\n")
          .append("\t</div>\n");
    });
    html.append("</div>\n");
    return html.toString();
  }

  File write_html() throws IOException {
    File html = new File(get_or_write_model_dir_in_www(), underscore_name + ".html");
    if (!html.exists()) html.createNewFile();
    try (FileWriter writer = new FileWriter(html)) {
      //TODO controls
      String complete_html = tpl_html.replace("[[CONTROLS]]", "")
          //TODO headers
          .replace("[[HEADER_ADD]]", underscore_name + "_add")
          .replace("[[MODEL_CONTENT_ADD]]", build_content_add())
          .replace("[[HEADER_EDIT]]", underscore_name + "_edit")
          .replace("[[MODEL_CONTENT_EDIT]]", build_content_edit());

      writer.write(complete_html);
    }
    return html;
  }

  File write_controller() throws IOException {
    File js = new File(get_or_write_model_dir_in_js_app(), underscore_name + ".ctrl.js");
    if (!js.exists()) js.createNewFile();
    try (FileWriter writer = new FileWriter(js)) {
      //TODO controls
      String complete_js = tpl_controller.replace("[[MODEL_NAME]]", underscore_name);
      writer.write(complete_js);
    }
    return js;
  }

  String col_ag() {
    String js_tmpl = "{\n" +
        "\tfield : '[[fname]]' , \n" +
        "\theaderName: '[[i18n]]' , \n" +
        "\tfilterParams:{ newRowsAction: 'keep' } \n" +
        "},\n";

    return String.join("\n", S._for(model.declaredFieldNames())
        .map(fname -> js_tmpl.replace("[[fname]]", fname)
                 //TODO
                 .replace("[[i18n]]", fname)
        ).join());
  }

  File write_aggrid() throws IOException {
    File js = new File(get_or_write_model_dir_in_js_app(), underscore_name + ".aggrid.js");
    if (!js.exists()) js.createNewFile();
    try (FileWriter writer = new FileWriter(js)) {
      //TODO controls
      String complete_js = tpl_aggrid
          .replace("[[MODEL_NAME]]", underscore_name)
          .replace("[[MODEL_COLS_AG]]", col_ag());
      writer.write(complete_js);
    }
    return js;
  }

  String ctrl_name() {
    return underscore_name + "_ctrl";
  }

  String aggrid_name() {
    return underscore_name + ".aggrid";
  }

  void write_browserify() throws IOException {
    List<String> lines = Files.readAllLines(Paths.get(browserify_location.toURI()));
    List<String> news = new ArrayList<>(2);

    int arrow_index = 0;

    for (; arrow_index < lines.size(); arrow_index++) {
      if (lines.get(arrow_index).contains(">")) break;
    }

    String js_app_prefix = "./js-app"
        + String.join("", S._for(class_paths).map(path -> "/" + path).join());

    news.add("\t\t-r " + js_app_prefix + "/" + underscore_name + ".ctrl.js:" + ctrl_name() + " \\");
    news.add("\t\t-r " + js_app_prefix + "/" + underscore_name + ".aggrid.js:" + aggrid_name() + " \\");

    lines.addAll(arrow_index, news);

    try (
        PrintWriter pw = new PrintWriter(Files.newBufferedWriter(Paths.get(browserify_location.toURI())))
    ) {
      for (String line : lines) {
        pw.println(line);
      }
    }
  }

  String build_ng_route() {
    String tpl = ".when('"
        + String.join("", S._for(class_paths).map(path -> "/" + path).join())
        + "',{ templateUrl: '[[tempURL]]', controller: require('[[ctrlName]]') })";

    return tpl.replace(
        "[[tempURL]]", "/modules"
            + String.join("", S._for(class_paths).map(path -> "/" + path).join())
            + "/" + underscore_name + ".html"
    ).replace("[[ctrlName]]", ctrl_name());
  }

  void write_main() throws IOException {
    List<String> lines = Files.readAllLines(Paths.get(angular_main_location.toURI()));
    //finding otherwise
    int otherwise_index = 0;

    for (; otherwise_index < lines.size(); otherwise_index++) {
      if (lines.get(otherwise_index).contains(".otherwise")) break;
    }

    lines.add(otherwise_index, build_ng_route());

    try (
        PrintWriter pw = new PrintWriter(Files.newBufferedWriter(Paths.get(angular_main_location.toURI())))
    ) {
      for (String line : lines) {
        pw.println(line);
      }
    }

  }

  public void build() throws IOException {
    write_html();
    write_aggrid();
    write_controller();
    write_main();
    write_browserify();
  }

  public static void main(String[] args) {
    S.echo("incoming params", args);
    if (args.length > 1) {
      try {
        try {
          if (args.length >= 3)
            new FrontScaffoldBuilder(args[0], args[1], args[2]).build();
          else
            new FrontScaffoldBuilder(args[0], args[1], null).build();
          S.echo("success");
        } catch (IOException e) {
          e.printStackTrace();
        }
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
        S.echo("class " + args[1] + "not found");
      }

    } else {
      S.echo("invalid params, p0 must be type, p1 must be the full class name of a valid record.");
    }


  }
}
